﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Npgsql;

namespace SpectationClient.SQLCommandBuilder {
    public class ConditionList : AbstractCondition {
        public enum Op:byte{
            AND,
            OR
        }

        private Op listOp;
        private List<Object> conditions;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="listOperator"></param>
        public ConditionList(Op listOperator) {
            this.init(listOperator);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="listOperator"></param>
        public ConditionList(Condition condition, Op listOperator) {
            this.init(listOperator);
            this.conditions.Add(condition);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="conditionList"></param>
        /// <param name="listOperator"></param>
        public ConditionList(ConditionList conditionList, Op listOperator) {
            this.init(listOperator);
            this.conditions.Add(conditionList);
        }

        private void init(Op listOperator) {
            this.listOp = listOperator;
            this.conditions = new List<object>();
        }

        public override bool IsEmpty {
            get {
                return this.conditions.Count == 0;
            }
        }
        public Op Operator { get { return this.listOp; } }



        /// <summary>
        /// Adds id values 
        /// </summary>
        /// <param name="idColumns"></param>
        /// <param name="idList"></param>
        public void AddIDs(IList<String> idColumns, List<Object[]> idList) {
            if(idList.Count == 0) return;
            int nIDs = idList[0].Length;
            if(nIDs != idColumns.Count()) throw new Exception("number of id columns differs from value per id tupel");
            
            if(nIDs > 1) {
                ConditionList clOuter = new ConditionList(ConditionList.Op.OR);
                foreach(Object[] ids in idList) {
                    ConditionList clInner = new ConditionList(ConditionList.Op.AND);
                    for(int i=0; i < nIDs; i++ ) {
                        clInner.Add(new Condition(idColumns[i],  Condition.Op.EQ, ids[i]));
                    }
                    clOuter.Add(clInner);        
                }
                this.Add(clOuter);

            } else { 
                Condition c = new Condition(idColumns[0], Condition.Op.EQ);
                foreach(Object[] ids in idList) {
                    c.Add(ids[0]);
                }
                this.Add(c);
                
            }
        }

        public void Add(Condition condition) {
            if(!condition.IsEmpty) this.Add((object)condition);

        }

        public void Add(GeometricCondition geometricCondition) {
            if(!geometricCondition.IsEmpty) this.Add((object)geometricCondition);
        }

        public void Add(ConditionList conditionList) {
            if(!conditionList.IsEmpty) this.Add((object)conditionList);
        }

        public void Add(SetCondition SetCondition) {
            if(!SetCondition.IsEmpty) this.Add((object)SetCondition);
        }

        public void Add(NpgsqlCommand cmd) {
            if(cmd.CommandText.Trim().Length != 0) this.Add((object)cmd);
        }

        private void Add(Object c) {
            if(c != null && !this.conditions.Contains(c)) {
                this.conditions.Add(c);
            }
        }

        private bool needBrackets(ConditionList condition) {
            Op opOut = condition.listOp;
            if(this.listOp == Op.AND && opOut == Op.OR) return true;
            return false;
        }

        public override NpgsqlCommand getCmd(String prefix) {
            return this.getCmd();
        }
        public override NpgsqlCommand getCmd() {
            NpgsqlCommand cmd = new NpgsqlCommand();
            
            if(this.conditions.Count == 1) {
                Object o = conditions[0];
                ConditionList cl = o as ConditionList;
                if(cl != null) return cl.getCmd();
                
                Condition c = o as Condition;
                if(c != null) return c.getCmd();

                GeometricCondition gc = o as GeometricCondition;
                if(gc != null) return gc.getCmd();

                SetCondition sc = o as SetCondition;
                if(sc != null) return sc.getCmd();

                NpgsqlCommand subCmd = o as NpgsqlCommand;
                if(subCmd != null) return subCmd;

                throw new NotImplementedException();

            } else if(this.conditions.Count > 1) {
                for(int i = 0; i < this.conditions.Count; i++) {
                    if( i > 0){
                        switch(this.listOp){
                            case Op.AND : cmd.CommandText += " AND "; break;
                            case Op.OR  : cmd.CommandText += " OR "; break;
                        }
                    }
                    
                    //String prefix2 = String.Format("{0}{1}", prefix, i);
                    
                    Object item = conditions[i];
                    NpgsqlCommand subCmd = item as NpgsqlCommand;
                    if(subCmd != null) {
                        CommandBuilder.appendCmd(ref cmd, subCmd);
                        continue;
                    }

                    ConditionList cl = item as ConditionList;
                    if(cl != null) {
                        NpgsqlCommand subCmd2 = cl.getCmd();
                        //Preserve Operator preference?
                        // (x = 3 OR x = 4) AND (y = 3 OR y = 5)
                        // -> need for bracket in case the sub-condition-list has operator OR and 
                        //    this condition-list Operator AND
                        if(this.needBrackets(cl)) subCmd.CommandText = String.Format("({0})", subCmd.CommandText);
                        CommandBuilder.appendCmd(ref cmd, cl);
                        continue;
                    }

                    AbstractCondition ac = item as AbstractCondition;
                    if(ac != null) { 
                        CommandBuilder.appendCmd(ref cmd, ac);
                        continue;
                    } 
                    throw new NotImplementedException();
                }
            }
            return cmd;
        }
    }
}
